/**
 * 
 */
package co.fitstart.mobile.service.impl;

import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections.MapUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.demo2do.core.BusinessException;
import com.demo2do.core.persistence.GenericDaoSupport;
import com.demo2do.core.service.impl.GenericServiceImpl;
import com.demo2do.core.utils.DateFormatUtils;

import co.fitstart.entity.coupon.Coupon;
import co.fitstart.entity.course.round.CourseRound;
import co.fitstart.entity.course.schedule.CourseSchedule;
import co.fitstart.entity.reference.Country;
import co.fitstart.entity.user.Student;
import co.fitstart.entity.user.User;
import co.fitstart.entity.user.UserAccount;
import co.fitstart.entity.user.WechatAccount;
import co.fitstart.entity.user.bwh.BwhRecord;
import co.fitstart.entity.user.inbody.InbodyRecord;
import co.fitstart.mobile.Constant;
import co.fitstart.mobile.service.UserService;

/**
 * @author downpour
 * @author lute
 *
 */
@Service("userService")
public class UserServiceImpl extends GenericServiceImpl<User> implements UserService {
    
    @Autowired
    private GenericDaoSupport genericDaoSupport;
    
    /**
     * get user by mobile
     * 
     * @param mobile
     * @return
     */
    private User getUserByMobile(String mobile) {
        String querySentence = "FROM User user WHERE user.mobile = :mobile AND user.disabled = false";
        return genericDaoSupport.searchForObject(querySentence, "mobile", mobile, User.class);
    }
    
    /**
     * 
     * @param openid
     * @return
     */
    private WechatAccount getWechatAccount(String openid) {
        return genericDaoSupport.searchForObject("FROM WechatAccount WHERE openid = :openid", "openid", openid, WechatAccount.class);
    }
    
    /*
     * (non-Javadoc)
     * @see co.fitstart.mobile.service.UserService#getUserbyMobile(java.lang.String)
     */
    public User getUser(String mobile) {
        return this.getUserByMobile(mobile);
    }
    
    /* (non-Javadoc)
     * @see co.fitstart.weixin.service.UserService#getUser(java.lang.String)
     */
    public User getUserByOpenId(String openid) {
        String querySentence = "FROM User user WHERE user.openid = :openid AND user.disabled = false";
        return genericDaoSupport.searchForObject(querySentence, "openid", openid, User.class);
    }
    
    /*
     * (non-Javadoc)
     * @see co.fitstart.mobile.service.UserService#getStudent(co.fitstart.entity.user.User)
     */
    public Student getStudent(User user) {
        String querySentence = "FROM Student student WHERE student.disabled = false AND student.user = :user";
        return genericDaoSupport.searchForObject(querySentence, "user", user, Student.class);
    }
    
    /*
     * (non-Javadoc)
     * @see co.fitstart.mobile.service.UserService#getStudent(java.lang.String)
     */
    public Student getStudent(String code) {
        String querySentence = "FROM Student WHERE disabled = false AND offlineCode = :code";
        Student student = genericDaoSupport.searchForObject(querySentence, "code", code, Student.class);
        
        if(student == null) {
            throw new BusinessException("输入的优惠码有误");
        }
        
        return student;
    }
    
    /*
     * (non-Javadoc)
     * @see co.fitstart.mobile.service.UserService#getUserAccount(java.lang.String, java.lang.String)
     */
    public UserAccount getUserAccount(String name, String value) {
        
        String querySentence = "FROM UserAccount userAccount WHERE userAccount.name = :name AND userAccount.value = :value";
        
        Map<String, Object> parameters = new HashMap<String, Object>(3);
        parameters.put("name", name);
        parameters.put("value", value);
        
        return genericDaoSupport.searchForObject(querySentence, parameters, UserAccount.class);
        
    }
    
    /*
     * (non-Javadoc)
     * @see co.fitstart.mobile.service.UserService#createUserAccount(co.fitstart.entity.user.UserAccount, co.fitstart.entity.user.WechatAccount)
     */
    public void createUserAccount(UserAccount userAccount, WechatAccount wechatAccount) {

        // create user account
        genericDaoSupport.save(userAccount);
        
        // create wechat account if not exit
        String openid = userAccount.getName();
        if(this.getWechatAccount(openid) == null) {
            genericDaoSupport.save(wechatAccount);
        }
    }
    
    /**
     * 
     * @param mobile
     * @return
     */
    @SuppressWarnings("unchecked")
    private List<InbodyRecord> listInbodyRecords(String mobile) {
        return genericDaoSupport.searchForList("FROM InbodyRecord WHERE disabled = false AND mobile = :mobile", "mobile", mobile);
    }
    
    /**
     * 
     * @param mobile
     * @return
     */
    @SuppressWarnings("unchecked")
    private List<Coupon> listCoupons(String mobile, String openid) {
        String sentence = "FROM Coupon coupon WHERE coupon.identification = :mobile";
        Map<String, Object> parameters = new HashMap<String, Object>();
        parameters.put("mobile", mobile);
        
        if(StringUtils.isNotEmpty(openid) && StringUtils.isNoneEmpty(openid)) {
            sentence = sentence + " OR coupon.openid = :openid";
            parameters.put("openid", openid);
        }
        
        return genericDaoSupport.searchForList(sentence, parameters);
    }
    
    /*
     * (non-Javadoc)
     * @see co.fitstart.mobile.service.UserService#login(java.lang.String, java.lang.String, java.lang.String, boolean)
     */
    public UserAccount login(String mobile, String openid, String accountId, boolean isEmail) {
        
        User user = this.getUserByMobile(mobile);
        
        // if user is empty, create a new user
        if(user == null) {
            user = new User(mobile, isEmail);
            genericDaoSupport.save(user);
        }
        
        // if openid exit, load userAccount by openid, bind userAccount's user and update user by wechatAccount info
        if(StringUtils.isNotEmpty(openid) && StringUtils.isNoneEmpty(openid)) {
            
            UserAccount openidUserAccount = this.getUserAccount(Constant.USER_ACCOUNT_WECHAT_KEY, openid);
            if(openidUserAccount != null) {
                openidUserAccount.bind(user);
                genericDaoSupport.update(openidUserAccount);
            }
            
            WechatAccount wechatAccount = this.getWechatAccount(openid);
            if(wechatAccount != null) {
                user.wechat(wechatAccount);
                genericDaoSupport.update(user);
            }
        }
        
        // if accountId is empty, create a new mobile userAccount and bind user
        UserAccount mobileUserAccount = this.getUserAccount(Constant.USER_ACCOUNT_MOBILE_KEY, mobile);
        if(mobileUserAccount == null) {
            mobileUserAccount = new UserAccount(Constant.USER_ACCOUNT_MOBILE_KEY, mobile);
            genericDaoSupport.save(mobileUserAccount.bind(user));
        }else {
            genericDaoSupport.update(mobileUserAccount.bind(user));
        }
        
        // if same mobile's inbodyRecords not null, bind user inbody records
        List<InbodyRecord> inbodyRecords = this.listInbodyRecords(mobile);
        
        if(CollectionUtils.isNotEmpty(inbodyRecords)) {
            for(InbodyRecord inbodyRecord : inbodyRecords) {
                genericDaoSupport.update(inbodyRecord.bindUser(user));
            }
        }
        
        // if same mobile's coupons not null, bind user coupons
        List<Coupon> coupons = this.listCoupons(mobile, openid);
        
        if(CollectionUtils.isNotEmpty(coupons)) {
            for(Coupon coupon : coupons) {
                genericDaoSupport.update(coupon.bindUser(user.getId()));
            }
        }
        
        return mobileUserAccount;
    }
    
    /*
     * (non-Javadoc)
     * @see co.fitstart.mobile.service.UserService#editRegion(co.fitstart.entity.user.User, java.lang.String, java.lang.String)
     */
    public void editRegion(User user, String country, String prd) {
        Country countryEnum = Country.privateValueOf(country);
        
        if(country.equalsIgnoreCase("OTHER") && StringUtils.isEmpty(prd)) {
            user.editRegion(countryEnum, null, null, null);
        } else {
            String[] prds = StringUtils.split(prd, ",");
            String provice = prds[0];
            String region= prds.length == 3 ? prds[1] : prds[0];
            String district = prds.length == 3 ? prds[2] : prds[1];
            
            user.editRegion(countryEnum, provice, region, district);
        }
        
        genericDaoSupport.update(user);
    }
    
    /*
     * (non-Javadoc)
     * @see co.fitstart.mobile.service.UserService#editAddress(co.fitstart.entity.user.User, java.lang.String)
     */
    public void editAddress(User user, String address) {
        genericDaoSupport.update(user.editAddress(address));
    }
    
    /*
     * (non-Javadoc)
     * @see co.fitstart.mobile.service.UserService#latestEffectiveDate(co.fitstart.entity.user.User)
     */
    @SuppressWarnings("unchecked")
    public Date latestEffectiveDate(User user) {
        
        Student student = this.getStudent(user);
        
        if(student == null) {
            return null;
        }
        
        List<CourseRound> courseRounds = genericDaoSupport.searchForList("FROM CourseRound WHERE disabled = false AND student = :student AND effectiveEnd IS NOT NULL ORDER BY effectiveEnd DESC", "student", student);
        
        if(CollectionUtils.isEmpty(courseRounds)) {
            return null;
        } else {
            return courseRounds.get(0).getEffectiveEnd();
        }
    }
    
    /*
     * (non-Javadoc)
     * @see co.fitstart.mobile.service.UserService#getLatestInbodyRecord(co.fitstart.entity.user.User)
     */
    @SuppressWarnings("unchecked")
    public InbodyRecord getLatestInbodyRecord(User user) {
        String sentence = "FROM InbodyRecord inbodyRecord WHERE inbodyRecord.disabled = false AND inbodyRecord.user = :user ORDER BY recordTime DESC";
        List<InbodyRecord> records = genericDaoSupport.searchForList(sentence, "user", user);
        
        return records.size() > 0 ? records.get(0) : null;
    }
    
    /*
     * (non-Javadoc)
     * @see co.fitstart.mobile.service.UserService#listInbodyRecord(co.fitstart.entity.user.User)
     */
    @SuppressWarnings("unchecked")
    public List<InbodyRecord> listInbodyRecord(User user) {
        String sentence = "FROM InbodyRecord inbodyRecord WHERE inbodyRecord.disabled = false AND inbodyRecord.user =:user ORDER BY recordTime ASC";
        List<InbodyRecord> records = genericDaoSupport.searchForList(sentence, "user", user);
        return records.size() > 0 ? records : null;
    }
    
    /*
     * (non-Javadoc)
     * @see co.fitstart.mobile.service.UserService#listInbodyRecoreds(co.fitstart.entity.user.User, co.fitstart.entity.course.round.CourseRound)
     */
    @SuppressWarnings("unchecked")
    public List<InbodyRecord> listInbodyRecoreds(User user, CourseRound courseRound) {
        
        List<InbodyRecord> inbodyRecords = new ArrayList<InbodyRecord>();
        
        Map<String, String> times = this.getTimes(courseRound);
        if(MapUtils.isNotEmpty(times)) {
            String sentence = "FROM InbodyRecord WHERE disabled = false AND user = :user AND recordTime BETWEEN :start AND :end ORDER BY recordTime DESC";
            Map<String, Object> parameters = new HashMap<String, Object>();
            parameters.put("user", user);
            parameters.put("start", times.get("startTime"));
            parameters.put("end", times.get("endTime"));
            inbodyRecords = genericDaoSupport.searchForList(sentence, parameters);
        }
        
        return inbodyRecords;
    }
    
    /*
     * (non-Javadoc)
     * @see co.fitstart.mobile.service.UserService#listBwhRecords(co.fitstart.entity.user.User, co.fitstart.entity.course.round.CourseRound)
     */
    @SuppressWarnings("unchecked")
    public List<BwhRecord> listBwhRecords(User user, CourseRound courseRound) {
        List<BwhRecord> bwhRecords = new ArrayList<BwhRecord>();
        
        if(courseRound.getCourseType().isOnline()) {
            String sentence = "FROM BwhRecord WHERE user = :user AND courseRound = :courseRound ORDER BY recordTime DESC";
            Map<String, Object> parameters = new HashMap<String, Object>();
            parameters.put("user", user);
            parameters.put("courseRound", courseRound);
            bwhRecords = genericDaoSupport.searchForList(sentence, parameters);
        } else {
            Map<String, String> times = this.getTimes(courseRound);
            if(MapUtils.isNotEmpty(times)) {
                String sentence = "FROM BwhRecord WHERE user = :user AND recordTime BETWEEN :start AND :end AND disabled = false ORDER BY recordTime DESC";
                Map<String, Object> parameters = new HashMap<String, Object>();
                parameters.put("user", user);
                parameters.put("start", times.get("startTime"));
                parameters.put("end", times.get("endTime"));
                bwhRecords = genericDaoSupport.searchForList(sentence, parameters);
            }
        }
        
        return bwhRecords;
    }
    
    /**
     * 
     * @param courseRound
     * @return
     */
    @SuppressWarnings("unchecked")
    private Map<String, String> getTimes(CourseRound courseRound) {
        Map<String, String> times = new HashMap<String, String>();
        
        String sentence = "FROM CourseSchedule WHERE disabled = false AND courseRound = :courseRound AND occurDate IS NOT NULL ORDER BY occurDate DESC";
        List<CourseSchedule> courseSchedules = genericDaoSupport.searchForList(sentence, "courseRound", courseRound);
        if(!courseSchedules.isEmpty()) { 
            times.put("startTime", DateFormatUtils.format(courseSchedules.get(courseSchedules.size()-1).getOccurDate(), "yyyy-MM-dd") + " 00:00:00");
            times.put("endTime", DateFormatUtils.format(courseSchedules.get(0).getOccurDate(), "yyyy-MM-dd") + " 23:23:59");
        }
        
        return times;
    }
}
